home *** CD-ROM | disk | FTP | other *** search
/ The Original Shareware 1.1 / The Original Shareware (WeMake CDs)(Volume 1.1)(CDs, Inc)(1993).iso / 11 / trs002.zip / TSRSH.ASM next >
Assembly Source File  |  1987-02-01  |  26KB  |  1,088 lines

  1. PAGE    60,132
  2. TITLE    Ram-Resident Program Shell, Preliminary Version (Lesson 2)
  3. SUBTTL    Date 02-01-87 -- Tutorial for Microsoft Forum
  4. ;-----------------------------------------------------------------------------
  5. ;    Ram-Resident Program Shell
  6. ;
  7. ;
  8. ;The software, documentation and source code are: 
  9. ;    (C) Copyright 1986, 1987
  10. ;    Chip Rabinowitz
  11. ;    All Rights Reserved 
  12. ;    51 East Rogues Path
  13. ;    Huntington Station NY 11746
  14. ;    Assistant SysOp, Microsoft Forum, Compuserve PPN 76703,350
  15. ;COPYRIGHT NOTICE AND WARRANTY INFORMATION 
  16. ;----------------------------------------- 
  17. ;This document ("the source code"), other accompanying written and disk-based
  18. ;notes and specifications ("the documentation"), and all referenced and related
  19. ;program files accompanying the source code and the documentation ("the
  20. ;software") are copyrighted by Chip Rabinowitz.  
  21. ;
  22. ;This program has been uploaded to Compuserve as part of a Tutorial in how
  23. ;to write Terminate-But-Stay-Resident programs, being conducted on
  24. ;Compuserve's Microsoft Special Interest Group during early 1987/
  25. ;
  26. ;This code (and all other code uploaded by the Copyright Holder as a 
  27. ;part of this TSR Tutorial) may be used with the following restrictions: 
  28. ;
  29. ;(1) If all or part of this code is used as part of a software package of 
  30. ;ANY kind, the following acknowledgement must be used in the Documentation 
  31. ;accompanying said package:
  32. ;
  33. ;    'Parts of the Resident Program Management for this Product are'
  34. ;    'Copyright (C) 1986, 1987 by Chip Rabinowitz, and are incorporated'
  35. ;    'into this product courtesy of the Ringmaster Development Team'
  36. ;
  37. ;(2) If all or part of this code is used as part of a software package that
  38. ;is placed in the public domain, no further restrictions apply.
  39. ;
  40. ;(3) If all or part of this code is used as part of a software package that
  41. ;is being distributed as 'shareware', a one-time-only donation of $10 will be
  42. ;accepted for the express purpose of continuing research into TSR standards.
  43. ;Note that this is a voluntary contribution, and should be sent to the
  44. ;Ringmaster Development Team at the address printed above.
  45. ;
  46. ;(4) If all or part of this code is used as part of a software package that
  47. ;will be distributed as a commercial product, a one-time-only payment of
  48. ;$25 will be accepted the express purpose of continuing research into TSR 
  49. ;standards.  Note that in THIS CASE ONLY, this is not a voluntary 
  50. ;contribution.  Commercial Developers are REQUIRED to make payment prior to
  51. ;incorporating these routines into their product.
  52. ;
  53. ;No copy of the source code may be distributed or given away without the
  54. ;accompanying documentation; and this notice must not be removed. 
  55. ;There is no warranty of any kind associated with this software, and the
  56. ;copyright owner is not liable for damages of any kind.  By using this
  57. ;software, you agree to this. 
  58. ;
  59.  
  60. XON     equ    11h
  61. XOFF    equ    13h
  62. TRUE    equ    1
  63. FALSE   equ    0
  64. CR    equ    0dh
  65. LF    equ    0ah
  66. TAB    equ    9
  67.  
  68.  
  69. HOTKEYON    equ    01h    ;hot key pressed
  70. SHIFTSON    equ    02h    ;shift states match
  71. TSRACTIVE    equ    04h    ;tsr is running in foreground
  72. INT28ACTIVE    equ    08h    ;INT28 routine is running in background
  73.  
  74. KB_DATA    equ    60h
  75. KB_CTL    equ    61h
  76. KB_FLG1    equ    17h
  77. KB_FLG2    equ    18h
  78. BIOS_DATA    equ    40h
  79.  
  80. code segment para public 'CODE'
  81.     org 100h
  82.     assume    cs:code, ds:code, es:code
  83. start:    jmp    begin
  84.  
  85.     db    1bh,'[2J'
  86. cpyrt    db    'Ram-Resident Program Shell, Preliminary Version (Lesson 2)',
  87.     db    CR,LF,LF
  88.     db    'Copyright (C) 1986, 1987 Chip Rabinowitz',
  89.     db    CR,LF,'All Rights Reserved',CR,LF,LF,'$'
  90. hello    db    'Hello World!',CR,LF,'$'
  91. bad_dos_msg    db    'Incompatible DOS version .... Aborting!',07h,CR,LF,'$'
  92.  
  93. tsr_parms:
  94.     PSP    dw    ?    ;PSP of TSR
  95.     OLDPSP    dw    ?    ;location to save current PSP
  96.     DTA    dd    ?    ;DTA of TSR
  97.     OLDDTA    dd    ?    ;location to save current DTA
  98.     DSEG    dw    ?    ;TSR's Data Segment
  99.     SSEG    dw    ?    ;TSR's Stack Segment ....
  100.     SPTR    dw    0fffeh    ;.... and Stack Pointer
  101.     ISSEG    dw    0    ;interrupted SS ...
  102.     ISPTR    dw    0    ;..... and SP
  103.     SHIFTST    db    0    ;Shift State to Activate
  104.     HOTKEY    db    0ffh    ;scan code to activate
  105.     STATUS    dw    0    ;current status words
  106.     POPUP    dd    0    ;far pointer to popup routine
  107.  
  108. intflags    db    0
  109. ININT13        equ    04h    ;Bios disk interrupt
  110. ININT21        equ    08h    ;DOS interrupt
  111.  
  112. dosversion    db    0    ;current version of DOS
  113. waitcount    db    0    ;wait to activate count
  114.  
  115. indosflag    dd    0    ;Pointer to INDOS flag
  116. doscriterr    dd    0    ;Pointer to DOS Critical Error Flag
  117.  
  118. dossseg        dw    0    ;pointer to dos stack segment
  119. dossptr        dw    0    ;pointer to dos stack pointer
  120. dossize        dw    0    ;dos stack size
  121.  
  122. dos21funcs    label    byte
  123.     db    0,    0,    0,    0,    0,    0,    0,    0    ;0-7
  124.     db    0,    0,    0,    0,    0,    0ffh,    0ffh,    0ffh    ;8-f
  125.     db     0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh    ;10-17
  126.     db    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh    ;18-1f
  127.     db    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0,    0ffh    ;20-27
  128.     db    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0    ;28-2f
  129.     db    0ffh,    0,    0,    0,    0,    0,    0ffh,    0ffh    ;30-37
  130.     db    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh    ;38-3f
  131.     db    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh    ;40-47
  132.     db    0,    0,    0,    0,    0ffh,    0,    0ffh,    0ffh    ;48-4f **
  133.     db    0,    0,    0,    0,    0ffh,    0,    0ffh,    0ffh    ;50-57
  134.     db    0,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0,    0    ;58-5f
  135.     db    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh,    0ffh    ;60-67
  136.  
  137. ;
  138. ; ***************************************************************************
  139. ;    These are the bios interrupt vectors
  140. ;
  141.  
  142. BIOSI8    equ    08h        ;Bios Timer interrupt
  143. BIOSI9    equ    09h        ;BIOS Hardware Keyboard interrupt
  144. BIOSIB    equ    0Bh
  145. BIOSIC    equ    0Ch
  146. BIOSI10    equ    10h
  147. BIOSI13    equ    13h        ;Bios Disk interrupt
  148. BIOSI14    equ    14h
  149. BIOSI15    equ    15h
  150. BIOSI16    equ    16h        ;Bios Keyboard interrupt
  151. BIOSI17    equ    17h
  152. BIOSI1C    equ    1Ch
  153. DOSI21    equ    21h        ;DOS service router interrupt
  154. DOSI28    equ    28h        ;DOS Idle interrupt
  155. DOSI33    equ    33h        ;mouse interrupt
  156. BIOSI1B    equ    1Bh
  157. DOSI23    equ    23h
  158. DOSI24    equ    24h
  159. ;
  160. oldint8        dd    0    ;BIOS Hardware Timer Interrupt
  161.         db    BIOSI8
  162.         dw    offset newint8    ;replacement vector
  163. oldint9        dd    0    ;BIOS Hardware Keyboard Interrupt
  164.         db    BIOSI9
  165.         dw    offset newint9    ;replacement vector
  166. oldint13    dd    0    ;BIOS Disk Interrupt
  167.         db    BIOSI13
  168.         dw    offset newint13    ;replacement vector
  169. oldint16    dd    0    ;BIOS Software Keyboard Interrupt
  170.         db    BIOSI16
  171.         dw    offset newint16    ;replacement vector
  172. oldint21    dd    0    ;DOS Services Interrupt
  173.         db    DOSI21
  174.         dw    offset newint21    ;replacement vector
  175. oldint28    dd    0    ;DOS Idle Interrupt
  176.         db    DOSI28
  177.         dw    offset newint28    ;replacement vector
  178. oldint1B    dd    0    ;control-C vector
  179.         db    BIOSI1B
  180.         dw    offset intret    ;replacement vector
  181. oldint23    dd    0    ;control-C vector
  182.         db    DOSI23
  183.         dw    offset intret    ;replacement vector
  184. oldint24    dd    0    ;critical error vector
  185.         db    DOSI24
  186.         dw    offset newint24    ;replacement vector
  187.  
  188. ;
  189. ; ***************************************************************************
  190. ;    Function to restore interrupts.
  191. ;
  192. ;    If AX=0, this function does popup-interrupts (1B, 23 and 24)
  193. ;    If AX=anything else, this function does initial interrupts
  194. ;
  195. restore_int proc near
  196.     push    ds
  197.     push    ax
  198.     push    dx
  199.     push    si
  200.     push    di
  201.     push    cx
  202.     mov    di,7            ;7 bytes per structure
  203.  
  204.     or    ax,ax
  205.     jnz    restore1
  206.  
  207.     mov    cx,3            ;three times through
  208.     mov    si,offset oldint1B    ;start at table beginning
  209.     jmp    restore_loop
  210. restore1:
  211.     mov    cx,06h            ;six interrupts!
  212.     mov    si,offset oldint8    ;top of table
  213.  
  214. restore_loop:
  215.     push    cx
  216.     push    di            ;save structure size
  217.     push    ds
  218.     mov    al,4[si]        ;interrupt number
  219.     mov    dx,[si]            ;IP of interrupt vector
  220.     mov    ds,2[si]        ;CS of interrupt vector
  221.     mov    ah,25h            ;set interrupt vector function
  222.     int    21h
  223.     pop    ds
  224.     xor    ax,ax            ;clear this entry from table
  225.     mov    [si],ax            ;so it shows not used
  226.     mov    2[si],ax
  227.     pop    di            ;get size back
  228.     add    si,di            ;do it again
  229.     pop    cx
  230.     loop    restore_loop
  231.  
  232.     pop    cx
  233.     pop    di
  234.     pop    si
  235.     pop    dx
  236.     pop    ax
  237.     pop    ds
  238.     ret
  239. restore_int endp
  240. ;
  241. ; ***************************************************************************
  242. ;    Function to set interrupts.
  243. ;
  244. ;    If AX=0, this function does popup-interrupts (1B, 23 and 24)
  245. ;    If AX=anything else, this function does initial interrupts
  246. ;
  247. setup_int proc near
  248.     push    es
  249.     push    ax
  250.     push    bx
  251.     push    dx
  252.     push    di
  253.     push    si
  254.     push    cx
  255.     mov    di,7            ;7 bytes per structure
  256.  
  257.     or    ax,ax
  258.     jnz    setup1
  259.  
  260.     mov    cx,3            ;three times through
  261.     mov    si,offset oldint1B    ;start at table beginning
  262.     jmp    setup_loop
  263. setup1:
  264.     mov    cx,06h            ;six interrupts!
  265.     mov    si,offset oldint8    ;top of table
  266.  
  267. setup_loop:
  268.     push    cx
  269.     push    di            ;save structure size
  270. ;    push    ds
  271.  
  272.     mov    ax,[si]            ;check for value
  273.     or    ax,ax            ;if not zero, next one
  274.     jnz    setup2
  275.     mov    al,4[si]        ;interrupt number
  276.     mov    ah,35h            ;get interrupt vector function
  277.  
  278.     int    21h
  279. ;    pop    ds
  280.     mov    [si],bx            ;save IP
  281.     mov    2[si],es        ;save CS
  282.  
  283.     mov    al,4[si]        ;interrupt number
  284.                     ;DS already contains correct segment
  285.     mov    dx,5[si]        ;IP of interrupt vector
  286.     mov    ah,25h            ;set interrupt vector function
  287.     int    21h
  288.  
  289. setup2:
  290.     pop    di            ;get size back
  291.     add    si,di            ;do it again
  292.     pop    cx
  293.     loop    setup_loop
  294. ex_set:
  295.     pop    cx
  296.     pop    si
  297.     pop    di
  298.     pop    dx
  299.     pop    bx
  300.     pop    ax
  301.     pop    es
  302.     ret
  303. setup_int endp
  304.  
  305. setdta    macro    ddptr
  306.     push    ds
  307.     mov    ax,1a00h    ;Function used to get current DTA address }
  308.     mov    dx,word ptr ddptr    ;Offset of DTA returned }
  309.     mov    ds,word ptr ddptr+2    ;Segment of DTA returned by DOS }
  310.     int    21h        ;Execute MSDos function request }
  311.     pop    ds
  312.     endm
  313.  
  314. getdta    macro    ddptr
  315.     push    es
  316.     mov    ax,2f00h    ;Function used to get current DTA address }
  317.     int    21h        ;Execute MSDos function request }
  318.     mov    word ptr ddptr+2,es        ;Segment of DTA returned by DOS }
  319.     mov    word ptr ddptr,bx        ;Offset of DTA returned }
  320.     pop    es
  321.     endm
  322.  
  323. ;
  324. ; ***************************************************************************
  325. ;    S E T   P S P
  326. ;
  327. ;    A bug in DOS 2.0, 2.1, causes DOS to clobber its standard stack   }
  328. ;    Then the PSP get/set functions are issued at the DOS prompt. The  }
  329. ;    following checks are made, forcing DOS to use the "critical"      }
  330. ;    stack when the TSR enters at the INDOS level.                     }
  331. ;
  332. ;        BX contains the PSP segment to set
  333. ;
  334. setpsp    proc    near
  335.     call    checkindos        ;If Version < 3, and INDOS,...
  336.     jz    do_set_psp
  337.     mov    byte ptr es:[di],0ffh    ;set critical flag
  338.  
  339.  
  340. do_set_psp:
  341.     mov    ax,5000h        ;set PSP* function
  342.                     ;BX already holds segment
  343.     int 21h                ;DOS function request
  344.  
  345.     call    checkindos        ;If Version < 3, and INDOS,...
  346.     jz    set_out
  347.     mov    byte ptr es:[di],0h    ;set critical flag
  348.  
  349. set_out:
  350.     ret
  351.  
  352. setpsp    endp
  353.  
  354. ;
  355. ; ***************************************************************************
  356. ;    G E T   P S P
  357. ;
  358. ;    A bug in DOS 2.0, 2.1, causes DOS to clobber its standard stack   }
  359. ;    Then the PSP get/set functions are issued at the DOS prompt. The  }
  360. ;    following checks are made, forcing DOS to use the "critical"      }
  361. ;    stack when the TSR enters at the INDOS level.                     }
  362. ;
  363. ;        The PSP Segment is returned in BX
  364. ;
  365. getpsp    proc    near
  366.     call    checkindos        ;If Version < 3, and INDOS,...
  367.     jz    do_get_psp
  368.     mov    byte ptr es:[di],0ffh    ;set critical flag
  369.  
  370. do_get_psp:
  371.     mov    ax,5100h        ;get PSP function
  372.     int 21h                ;DOS function request
  373.                     ;BX now holds PSP segment
  374.     call    checkindos        ;If Version < 3, and INDOS,...
  375.     jz    get_out
  376.     mov    byte ptr es:[di],0h    ;clear critical flag
  377.  
  378. get_out:
  379.     ret
  380.  
  381. getpsp    endp
  382.  
  383. ;
  384. ; ***************************************************************************
  385. ;    This routine checks the version of DOS in use, and returns the
  386. ;    ZF=0 (Not zero) if version < 3.0, and INDOS is set.  Internal function
  387. ;    is called ONLY by getpsp and setpsp.
  388. ;
  389. checkindos proc near
  390.     mov    al,dosversion
  391.     cmp    al,3            ;If Version is less than 3.0 ...
  392.     jge    ok_ret
  393.     mov    es,word ptr indosflag+2    ; ... and ...
  394.     mov    di,word ptr indosflag
  395.     mov    al,byte ptr es:[di]
  396.     or    al,al            ;INDOS is set ...
  397.     jz    ok_ret
  398.     mov    es,word ptr doscriterr+2; ... then ...
  399.     mov    di,word ptr doscriterr
  400.     jmp    fixit            ;exit with not zero
  401. ok_ret:
  402.     xor    al,al            ;set zero flag
  403. fixit:
  404.     ret
  405.  
  406. checkindos endp
  407.  
  408. ;
  409. ; ***************************************************************************
  410. ;    Internal routine to establish addressibility of the data segment
  411. ;
  412. dds proc near
  413.     push    cs
  414.     pop    ds
  415.     ret
  416. dds endp
  417.  
  418. newint9 proc far
  419.  
  420.     pushf
  421.     cli
  422.     push    ds
  423.     push    es
  424.     push    ax
  425.     push    bx
  426.     push    cx
  427.     push    dx
  428.     push    di
  429.     push    si
  430.  
  431.     push    cs
  432.     pop    ds
  433.  
  434. ;internal INT9 processing here
  435.  
  436. getkey_out:
  437.     pop    si
  438.     pop    di
  439.     pop    dx
  440.     pop    cx
  441.     pop    bx
  442.     pop    ax
  443.     pop    es
  444.     pop    ds
  445. out_9:
  446.     popf
  447.  
  448.     pushf
  449.     cli
  450.     call    dword ptr cs:oldint9
  451.  
  452.  
  453. ;
  454. ; ***************************************************************************
  455. ;    This is the int return (IRET) dummy pointer 
  456. ;
  457. intret:
  458.     iret
  459.  
  460. ignore_9:
  461.     in    al,60h
  462.     push    ax
  463.     pop    ax
  464.     in    al,61h
  465.     mov    ah,al
  466.     or    al,80h
  467.     out    61h,al
  468.     xchg    ah,al
  469.     out    61h,al
  470.     mov    al,20h
  471.     out    20h,al
  472.     pop    ax
  473.     jmp    short out_9
  474.  
  475. newint9 endp
  476.  
  477. new_bios_flag    db    0
  478.  
  479. newint16 proc far
  480. start_again:
  481.     mov    new_bios_flag,0
  482.     cmp    ah,00        ;if char request,
  483.     je    func00        ;loop for character
  484.     cmp    ah,01        ;if character availability test
  485.     je    func01        ;go check for char
  486.     and    ah,10h        ;test for extra bios
  487.     jz    gobios16
  488.     mov    new_bios_flag,10h
  489.     and    ah,3
  490.     jmp    short start_again
  491. gobios16:
  492.     or    ah,new_bios_flag
  493.     jmp    dword ptr cs:[oldint16]
  494.                 ;go to bios interrupt 16
  495. func01:
  496.     push    ds
  497.     push    si
  498.     push    di
  499.     call    dds
  500. func01A:
  501.     call    keystat        ;look at key buffer
  502.     pushf            ;save return flags
  503.     jz    fret01        ;return if no key
  504.     call    hotcheck    ;test for hot key
  505.     jnz    fret01        ;not there
  506.     popf            ;flags back for loop
  507.     jmp    func01A        ;it was a hot key -- skip it!
  508. fret01:
  509.     popf            ;flags back
  510.     pop    di
  511.     pop    si
  512.     pop    ds
  513.     ret    2        ;return to user
  514. func00:
  515.     push    ds
  516.     push    si
  517.     push    di
  518.     call    dds
  519. func00A:
  520.     call    keystat        ;wait until character available
  521.     jz    func00A        ;loop it
  522.     call    hotcheck    ;test for a hot key
  523.     jz    func00A        ;it sure was -- start loop again
  524.  
  525. b_func0:
  526.     mov    ah,0        ;get the next user key
  527.     or    ah,new_bios_flag
  528.     pushf            ;simulate it ...
  529.     call    dword ptr cs:oldint16    ;do bios
  530.  
  531. func_out:
  532.     pushf            ;save return flags
  533.     jmp    fret01        ;restore and return
  534.  
  535. newint16 endp
  536.  
  537. ;
  538. ;       call the background task if no key is available
  539. ;
  540. keystat proc near
  541.                 ;preserves all registers except
  542.                 ;AX,SI,DI,DS & flags
  543.     push    cx
  544.     push    bx
  545.  
  546.     push    ax
  547.     push    ds
  548.     cld
  549.  
  550.     mov    ax,BIOS_DATA
  551.     mov    ds,ax        ;set data segment
  552.     mov    si,KB_FLG1    ;set source
  553.  
  554.     lodsw            ;get the high and low bytes
  555.     pop    ds
  556.     mov    bx,ax        ;save it away
  557.     pop    ax
  558.  
  559.  
  560.     mov    al,SHIFTST    ;shift state check
  561.  
  562.     or    al,al        ;is it zero?
  563.     jz    sim_16        ;yes -- set flag for simulation
  564.  
  565.     push    bx        ;save current flags
  566.     and    bl,al        ;mask bits
  567.     cmp    al,bl        ;are we the same?
  568.     pop    bx
  569.     jne    skip_16A    ;nope -- next one
  570. sim_16:
  571.     or    word ptr STATUS,SHIFTSON    ;set the flag
  572.  
  573.     mov    al,HOTKEY    ;is this control block the last one?
  574.  
  575.     or    al,al
  576.     jnz    short skip_16
  577.     or    word ptr STATUS,HOTKEYON    ;turn on hotkey flag
  578.     jmp    short skip_16
  579. skip_16A:
  580.     and    word ptr STATUS,NOT SHIFTSON    ;set the flag
  581.  
  582. skip_16:
  583.     mov    ah,1            ;see if character is available
  584.     or    ah,new_bios_flag
  585.     pushf                ;simulate interrupt
  586.     call    dword ptr cs:oldint16
  587.  
  588.     jnz    exit_161        ;got one
  589.  
  590. chkdos_161:
  591.     cld
  592.     push    ds            ;check if dos critical error in effect
  593.     push    si
  594.         lds     si,cs:doscriterr    ;zero says dos is interruptable
  595.     lodsb                ;$ff  says dos is in a critical state
  596.     lds     si,cs:indosflag        ;if indos then int $28 issued by dos
  597.     or    al,[si]            ;so we dont have to.
  598.                     ;account for active interrupts
  599.     or    al,cs:intflags        ;any flags says we dont issue call
  600.     pop    si            ;to the background.
  601.     pop    ds
  602.     cmp    al,01            ;must be indos flag only
  603.     jg    skip28            ;dos cannot take an interrupt yet
  604.     int    28h            ;call dos idle function (background dispatch).
  605. skip28:
  606.     xor    ax,ax            ;show  no keycode available
  607. exit_161:
  608.     pop    bx
  609.     pop    cx
  610.         ret
  611.  
  612. keystat    endp
  613.  
  614. hotcheck proc near    ;DI points to TSR Block character is from
  615.             ;SI holds ID of TSR routine
  616.  
  617.     push    cx
  618.     push    ax        ;save character
  619.     push    di
  620.  
  621.     mov    al,HOTKEY
  622.  
  623.     or    al,al
  624.     jz    only_shift    ;special case for shift-state only
  625.  
  626.     cmp    al,ah        ;do scan codes match?
  627.     jne    again_16H
  628.  
  629. only_shift:
  630.     push    ax
  631.     mov    ax,STATUS    ;get TSR status flags
  632.     test    ax,SHIFTSON    ;are shift codes set?
  633.     jz    again_16H1    ;nope -- try next one
  634.     
  635.     or    ax,HOTKEYON    ;turn on hotkey flag
  636.     mov    STATUS,ax    ;put new flags back
  637.  
  638.     pop    ax
  639.     or    al,al        ;test for special case
  640.     pop    di        ;get back Block pointer
  641.     pop    ax        ;throwing this away
  642.  
  643.     jz    hot_out        ;special case for flags only
  644.  
  645. b_func1:
  646.     mov    ah,0        ;get rid of the character
  647.     or    ah,new_bios_flag
  648.     pushf            ;simulate interrupt
  649.     call    dword ptr cs:oldint16    ;do bios
  650.  
  651. hot_out:
  652.     xor    ax,ax        ;set zero flag
  653.     jmp    got_hot
  654.  
  655. again_16H1:
  656.     pop    ax        ;get rid of flag
  657. again_16H:
  658.     mov    al,0ffh
  659.     or    al,al        ;clear zero flag if set
  660.     pop    di
  661.     pop    ax        ;get back keystroke
  662. got_hot:
  663.     pop    cx        ;get back cx register
  664.     ret
  665.  
  666. hotcheck endp
  667.  
  668. newint8 proc far
  669.  
  670.     pushf
  671.     push    ds
  672.     push    di
  673.     push    cx            ;save regs
  674.     push    ax
  675.     call    dds
  676.  
  677.     mov    ax,STATUS
  678.     test    ax,HOTKEYON    ;is hot-key flag set?
  679.     jz    again_8_2
  680.     test    ax,TSRACTIVE    ;are we already running?
  681.     jnz    again_8_2    ;yep -- next one
  682.  
  683. wait_8:
  684.     cmp    waitcount,0    ;if waiting, check time
  685.     jnz    wait2_8        ;decrement count
  686.  
  687. chkdos_8:
  688.     cld
  689.     push    ds            ;check if dos critical error in effect
  690.     push    si
  691.         lds     si,cs:doscriterr    ;zero says dos is interruptable
  692.     lodsb                ;$ff  says dos is in a critical state
  693.     lds     si,cs:indosflag        ;add in second status byte
  694.     or    al,[si]
  695.     or    al,cs:intflags        ;any flags says we're busy
  696.     pop    si
  697.     pop    ds
  698.     jnz    wait1_8            ;we're busy -- set count & EXIT
  699.     call    dopopup            ;call the popup stuff
  700.     jmp    end_8            ;and out we go
  701. wait1_8:
  702.     mov    waitcount,10h        ;set the count
  703. wait2_8:
  704.     dec    waitcount
  705.     jz    chkdos_8
  706.  
  707. again_8_2:
  708. end_8:
  709.     pushf                ;simulate interrupt ...
  710.     call dword ptr oldint8        ;do the original
  711.  
  712.     pop    ax        ;get back registers
  713.     pop    cx
  714.     pop    di
  715.     pop    ds
  716.     popf
  717.  
  718. exit_8:
  719.     iret
  720.  
  721. newint8 endp
  722.  
  723. newint28 proc far
  724.  
  725.     pushf                ;simulate interrupt ...
  726.     call dword ptr cs:oldint28    ;do the original
  727.  
  728.     push    ds
  729.     push    di
  730.     push    cx            ;save regs
  731.     push    ax
  732.  
  733.     push    cs
  734.     pop    ds
  735.  
  736. again_28:
  737.     mov    ax,STATUS    ;get TSR status flags
  738.     test    ax,HOTKEYON    ;is hot-key flag set?
  739.     jz    again_28_2
  740.     test    ax,TSRACTIVE    ;are we already running?
  741.     jnz    again_28_2    ;yep -- next one
  742.  
  743. chkdos_28:
  744.     cld
  745.     push    ds            ;check if dos critical error in effect
  746.     push    si
  747.         lds     si,cs:doscriterr    ;zero says dos is interruptable
  748.     lodsb                ;$ff  says dos is in a critical state
  749.     or    al,cs:intflags        ;any flags says we're busy
  750.                     ;NOTE:  Indos flag is always set here!
  751.                     ; ... so don't have to check it!
  752.     pop    si
  753.     pop    ds
  754.     jnz    end_28            ;we're busy-EXIT & try again next time
  755.  
  756.     mov    waitcount,0        ;clear waiting count and ...    
  757.     call    dopopup            ;... call the popup stuff
  758.     jmp    end_28            ;and out we go
  759.  
  760. again_28_2:
  761. end_28:
  762.     pop    ax        ;get back registers
  763.     pop    cx
  764.     pop    di
  765.     pop    ds
  766.  
  767. exit_28:
  768.     iret
  769.  
  770. newint28 endp
  771.  
  772. di28    dw    0
  773. ds28    dw    0
  774.  
  775. dopopup proc near
  776.  
  777.     cli            ;no interrupts now!!!
  778.     
  779.     or    word ptr STATUS,TSRACTIVE    ;set active bit
  780.  
  781.     mov    ISSEG,ss        ;save user's stack segment
  782.     mov    ISPTR,sp        ;... and stack pointer
  783.                     ;DS was saved in INT8 or INT28
  784.  
  785.     mov    ss,SSEG        ;load TSR stack segment ...
  786.     mov    sp,SPTR        ;... and stack pointer
  787.  
  788.     push    bp            ;save all registers except for
  789.     push    bx            ;...those saved by calling routine,
  790.     push    dx            ;...DS,DI,CX,AX are restored on exit
  791.     push    si            ;...from this procedure
  792.     push    es
  793.  
  794.     mov    [di28],di        ;save TSR pointer until after saving
  795.     mov    [ds28],ds        ;...Indos stack
  796.  
  797.     mov    di,sp            ;destination is our stack
  798.     dec    di
  799.     dec    di            ;back off current word
  800.     mov    ax,ss            ;move stack segment
  801.     mov    es,ax            ;...to extra segment
  802.  
  803.     mov    si,cs:dossptr        ;source is DOS Indos Primary Stack
  804.     mov    ds,cs:dossseg        ;...and segment, of course
  805.     dec    si            ;point to last word on DOS stack
  806.     dec    si
  807.  
  808.     mov    cx,40h            ;stack size to save
  809.  
  810.     mov    ax,sp            ;save current stack pointer ...
  811.     sub    ax,cx            ;...and make room to avoid
  812.     sub    ax,cx            ;...overwriting during REP
  813.     mov    sp,ax            ;...and put it back
  814.  
  815.     std                ;set the direction flag ...
  816.     rep    movsw            ;and move the 40 word stack
  817.     mov    sp,di
  818.     cld                ;direction flag back
  819.  
  820.     mov    di,cs:di28        ;get TSR pointer back
  821.     mov    ds,cs:ds28
  822.  
  823.     sti                ;interrupts back on!!
  824.  
  825.     getdta    OLDDTA            ;save current DTA
  826.  
  827.     call    getpsp            ;get current PSP segment
  828.     mov    OLDPSP,bx        ;...returned in BX
  829.  
  830.     mov    bx,PSP            ;set TSR's PSP in BX
  831.     call    setpsp            ;...and do it
  832.  
  833.     setdta    DTA
  834.  
  835.     xor    ax,ax
  836.     call    setup_int        ;replace with our handler
  837.  
  838.     push    ds            ;save DS:DI for after popup
  839.     push    di
  840.     mov    ds,DSEG        ;load user's data segment
  841.  
  842.  
  843.     call    dword ptr cs:POPUP    ;call the user routine!!
  844.  
  845.  
  846.     pop    di            ;restore ds:di for addressibility
  847.     pop    ds
  848.  
  849.     mov    bx,OLDPSP        ;replace interrupted PSP
  850.     call    setpsp
  851.  
  852.     setdta    OLDDTA        ;restore interrupted DTA
  853.  
  854.     xor    ax,ax
  855.     call    restore_int        ;restore interrupts as well
  856.  
  857. ;terminate checking here!
  858.  
  859.     cli                ;no interrupts while manipulating stack
  860.  
  861.     mov    cx,40h            ;stack size here
  862.  
  863.     mov    di28,di            ;save TSR pointer until after saving
  864.     mov    ds28,ds            ;...Indos stack
  865.  
  866.     mov    ax,ss            ;source is current stack
  867.     mov    ds,ax
  868.     mov    si,sp            ;point to last word on our stack
  869.     inc    si
  870.     inc    si
  871.  
  872.     mov    es,cs:dossseg        ;destination is dos stack
  873.     mov    di,cs:dossptr
  874.     sub    di,cx            ;point backward to last used word 
  875.     sub    di,cx            ;...on dos stack
  876.     cld
  877.     rep    movsw
  878.  
  879.     mov    sp,si            ;skip over moved words
  880.  
  881.     mov    di,cs:[di28]        ;get TSR pointer back
  882.     mov    ds,cs:[ds28]
  883.  
  884.     pop    es
  885.     pop    si
  886.     pop    dx
  887.     pop    bx
  888.     pop    bp
  889.  
  890. ;
  891. ; ***************************************************************************
  892. ;
  893. ;    CAUTION!!!! THE USER ROUTINE MUST RETURN SS:SP WITH THE SAME
  894. ;        VALUES AS ON ENTRY.  OTHER REGISTERS WILL BE RESTORED
  895. ;        BY THIS ROUTINE.
  896. ;
  897. ; ***************************************************************************
  898. ;
  899. back_popup:
  900.  
  901.     mov    ss,ISSEG        ;restore user's stack segment
  902.     mov    sp,ISPTR        ;... and stack pointer
  903.                     ;DS will be restored in INT8 or INT28
  904.     and    STATUS,NOT HOTKEYON+TSRACTIVE
  905.                     ;clear hot-key and inuse bits
  906.  
  907.     sti                ;enable interrupts again
  908. dopopup_end:
  909.     ret
  910.  
  911. dopopup endp
  912.  
  913. newint13 proc far
  914.  
  915.     pushf
  916.     or     cs:intflags,ININT13
  917.     popf            ;get back original flags
  918.  
  919.     pushf                ;simulate interrupt ...
  920.     call dword ptr cs:oldint13    ;do the original
  921.  
  922.     pushf
  923.     and    cs:intflags,NOT ININT13
  924.     popf
  925.  
  926.     ret    2
  927.  
  928. newint13 endp
  929.  
  930. newint21 proc far
  931.     pushf
  932.     sti                ;allow interrupts
  933.  
  934.     cmp    ah,63h            ;max number
  935.     jg    notme
  936.     push    ax            ;some INT21 functions must be left
  937.     push    bx            ;...alone.  They either never return,
  938.     mov    bx,offset dos21funcs    ;...grab parameters from the stack
  939.     mov    al,ah            ;...or can be interrupted.
  940.     xlat    cs:dos21funcs        ;test function against table
  941.     or    al,al            ;wait for functions marked zero
  942.     pop    bx            ;these ,ust be left alone
  943.     pop    ax
  944.     jz    notme            ;jump to original INT21
  945.     jmp    short set_21
  946. notme:
  947.     popf
  948.     jmp    dword ptr cs:oldint21
  949.  
  950. set_21:
  951.     or     cs:intflags,ININT21    ;say int21 is active
  952.     popf                ;get original flags back
  953.     pushf                ;save flags and all regs that
  954.  
  955.     call    dword ptr cs:oldint21    ;do the interrupt
  956.     sti
  957.  
  958.     pushf                ;save return registers
  959.     and    cs:intflags,NOT ININT21    ;not active any more
  960.     popf                ;they're back
  961.  
  962.     ret    2            ;return with flags set
  963.  
  964. newint21 endp
  965.  
  966. error24    dw    0
  967.  
  968. newint24 proc far
  969.     pushf
  970.     mov    cs:error24,di        ;save the error
  971.     mov    al,0
  972.     iret
  973.  
  974. newint24 endp
  975.  
  976. pop_routine proc far    ;just prints a msg and gets out
  977.     lea    dx,hello
  978.     mov    ah,9
  979.     int    21h        ; display copyright notice
  980.     ret
  981. pop_routine endp
  982.  
  983.  
  984. TSR_stack     db    512 dup(0)
  985. TSR_stack_end:
  986.  
  987. res_part:    ; this is the end of my resident code so I can tell DOS
  988.         ; how big I am
  989. ;
  990. ;
  991. ; ***************************************************************************
  992. ;
  993. ;    Initialization Code -- load routine and point new int14
  994. ;
  995. second_time    db    0    ;flag used to test for odd-byte check
  996.  
  997. begin:
  998.     lea    dx,cpyrt
  999.     mov    ah,9
  1000.     int    21h        ; display copyright notice
  1001.  
  1002.     call    getpsp        ;Current PSP returned in BX
  1003.     mov    PSP,bx
  1004.     getdta    DTA        ;macro to get Disk Transfer Address
  1005.  
  1006.     mov    ax,ds
  1007.     mov    DSEG,ax        ;save it
  1008.     mov    SSEG,ax        ;save it here too!
  1009.     mov    word ptr POPUP+2,ax    ;save it here three!
  1010.     mov    ax,offset TSR_stack_end
  1011.     sub    ax,2
  1012.     mov    SPTR,ax
  1013.  
  1014.     mov    SHIFTST,08h    ;ALT-Key + ...
  1015.     mov    HOTKEY,2dh    ;...'X' to pop up
  1016.  
  1017.     mov    word ptr POPUP,offset pop_routine
  1018.  
  1019.     mov    ax,3400h        ;find the DOS Indos Byte
  1020.     int    21h
  1021. crit2:
  1022.     mov    word ptr indosflag,bx    ;offset returned in BX
  1023.     mov    word ptr indosflag+2,es    ;segment returned in ES
  1024.     mov    dossseg,es        ;this is also the DOS stack segment
  1025.     mov    word ptr doscriterr+2,es;as well as the segment for the critical flag
  1026.     mov    cx,2000h         ;search for instruction: CMP [crit flag],00
  1027.     mov    ax,3e80h         ;3e80 is opcode to search for
  1028.     mov    di,bx             ;start search here
  1029. crit3:
  1030.     repnz    scasw             ;search until a match is found
  1031.     jnz    no_crit_found         ;couldn't find critical byte
  1032.                     ;we're going to search for the
  1033.                     ;address of the critical flag
  1034.                     ;es:[di-2] now points to:
  1035.                     ;       CMP [crit flag],00
  1036.                     ;       JNZ ...
  1037.                     ;       MOV SP,indos stack address      
  1038.     mov    al,byte ptr es:[di+5]     ;MOV SP,xxxx
  1039.     cmp    al,0bch         ;opcode for MOV SP
  1040.     jne    crit3             ;bad place.  Sorry
  1041.     mov    ax,word ptr es:[di]     ;here's the critical byte address
  1042.     mov    word ptr doscriterr,ax
  1043.     mov    ax,word ptr es:[di+6]     ;...and here's the stack pointer
  1044.     mov    word ptr dossptr,ax
  1045.     mov    ax,1                                                     
  1046.     jmp    no_error
  1047. no_crit_found:
  1048.     mov    al,second_time        ;did we already try odd bytes?
  1049.     or    al,al
  1050.     jz    crit_again        ;nope ...
  1051.     jmp    bad_dos_version        ;we tried!
  1052. crit_again:
  1053.     mov    second_time,1        ;set the flag
  1054.     inc    bx            ;increment pointer (needed for AT&T
  1055.     jmp    crit2            ;DOS 2.11 and some others
  1056.  
  1057. no_error:
  1058.     mov    ax,1
  1059.     call    setup_int
  1060.  
  1061.     lea    dx,res_part    ; load offset of top of code
  1062.     add    dx,15        ;round up
  1063.     shr    dx,1
  1064.     shr    dx,1
  1065.     shr    dx,1
  1066.     shr    dx,1        ; divide by 16 to get paragraphs
  1067.     xor    al,al        ; no errors
  1068.     mov    ah,31h        ; load terminate & stay resident code
  1069.     int    21h
  1070.  
  1071. bad_dos_version:
  1072.     lea    dx,bad_dos_msg
  1073.     mov    ah,9
  1074.     int    21h        ; display error message
  1075.     mov    ax,4cffh            ;exit with -1 return code
  1076.     int    21h
  1077.  
  1078. code    ends
  1079.  
  1080.     end    start
  1081.